package net.e_agri.wap;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.appengine.api.urlfetch.FetchOptions;
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;

public class HttpMethod {

	private static final Log log = LogFactory.getLog(HttpMethod.class);

	private static final int MAX_RETRIES = 5;

	public static String doGet(String url) throws IOException{

		String uri = url;
		if(!uri.startsWith("http"))
			uri = "http://0.0.0.0:443/" + uri;

		uri = uri.replace("/./?", "/?");
		log.info("opening " + uri + ", MAX_RETRIES = " + MAX_RETRIES);
		StringBuffer buf = new StringBuffer();
		
		InputStream is = null;
		int retries = 0;
		while(retries <= MAX_RETRIES){
			try{
				is = new URL(uri).openStream();

				BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
				String line = null;
		       while((line = br.readLine()) != null)
		                buf.append(line).append("\n");

				break;
			}catch(IOException ie){
				log.info("IOException:" + ie.getMessage());
				ie.printStackTrace();
			    try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			retries++;
			log.info("retries : " + retries);
		}

       return buf.toString();
	}

	public static String doPost(String uri) throws IOException{
		return doPost(uri, new HashMap<String,String>());
	}
	
	public static Map<String, String> doGetAsync(List<String> urlList) throws IOException{

		List<URL> urlslist = new ArrayList<URL>();
		for(String url : urlList)
			urlslist.add(new URL(url.trim()));

		return doGetAsyncByURL(urlslist);
	}

	private static Map<String, String> doGetAsyncByURL(List<URL> urlList) throws IOException{

		Map<String, String> returnMap = new HashMap<String, String>();
		Map<String, Future<HTTPResponse>> futureResponseMap = new LinkedHashMap<String, Future<HTTPResponse>>();

		URLFetchService ursvc = URLFetchServiceFactory.getURLFetchService();
		HTTPRequest ureq = null;
		HTTPResponse ures = null;
		for(URL url : urlList){
				ureq = new HTTPRequest(url, HTTPMethod.GET,
					FetchOptions.Builder
					.withDeadline(10.0)
					.followRedirects()
					.allowTruncate()
					.doNotValidateCertificate());
				log.info("fetchAsync : " + url.toString());
				futureResponseMap.put(url.toString(), ursvc.fetchAsync(ureq));
		}

		for(String url : futureResponseMap.keySet()){
			try {
				ures = futureResponseMap.get(url).get();
				log.info("  Got response : " + ures.getResponseCode());
				returnMap.put(url, new String(ures.getContent()));
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		}

		return returnMap;
	}

	public static Future<HTTPResponse> doPostAsync(String uri, Map<String,String> params) throws IOException{
	
		URL url = new URL(uri);
		log.info("async opening " + url.toString());
		boolean first = true;

		StringBuffer paramBuf = new StringBuffer();
		URLFetchService ursvc = URLFetchServiceFactory.getURLFetchService();
		HTTPRequest ureq = new HTTPRequest(url, HTTPMethod.POST,FetchOptions.Builder
				.withDeadline(10.0)
				.followRedirects()
				.allowTruncate()
				.doNotValidateCertificate());
		//ureq.addHeader(new HTTPHeader("Content-Type", "text/html; charset=utf-8"));
		for(String paramName : params.keySet()){
			log.info(String.valueOf(first?"":"&") + paramName + "=" + params.get(paramName));
			paramBuf.append(String.valueOf(first?"":"&")).append(paramName)
					.append("=").append(params.get(paramName));
			first = false;
		}
		ureq.setPayload(paramBuf.toString().getBytes());

		return ursvc.fetchAsync(ureq);

		/*
		Queue que = QueueFactory.getDefaultQueue();
		TaskOptions postTaskOps = Builder.withUrl("/wap").method(Method.GET);
		for(Object paramName : req.getParameterMap().keySet())
			postTaskOps.param(paramName.toString(), req.getParameter(paramName.toString()));
		postTaskOps.taskName("login");
		que.add(postTaskOps);
		*/
	}
	
	private static String doPostByURLFetch(String uri, Map<String,String> params) throws IOException{

		URL url = new URL(uri);
		log.info("opening " + url.toString() + ", MAX_RETRIES = " + MAX_RETRIES);
		boolean first = true;

		StringBuffer paramBuf = new StringBuffer();
		URLFetchService ursvc = URLFetchServiceFactory.getURLFetchService();
		HTTPRequest ureq = new HTTPRequest(url,
			HTTPMethod.POST,
			FetchOptions.Builder
				.withDeadline(10.0)
				.followRedirects()
				.allowTruncate()
				//.doNotFollowRedirects()
				.doNotValidateCertificate());

		ureq.addHeader(new HTTPHeader("Host", "agri-mobile.appspot.com"));
		ureq.addHeader(new HTTPHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"));
		ureq.addHeader(new HTTPHeader("Accept-Language", "ja,en;q=0.7,en-us;q=0.3"));
		ureq.addHeader(new HTTPHeader("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"));
		ureq.addHeader(new HTTPHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:5.0) Gecko/20100101 Firefox/5.0"));
		ureq.addHeader(new HTTPHeader("Content-type", "application/x-www-form-urlencoded"));

		for(String paramName : params.keySet()){
			log.info(String.valueOf(first?"?":"&") + URLEncoder.encode(paramName, "UTF-8")
					+ "=" + URLEncoder.encode(params.get(paramName), "UTF-8"));
			paramBuf.append(String.valueOf(first?"?":"&")).append(URLEncoder.encode(paramName, "UTF-8"))
					.append("=").append(URLEncoder.encode(params.get(paramName), "UTF-8"));
			first = false;
		}
		ureq.setPayload(paramBuf.toString().getBytes("UTF-8"));
		
		for(HTTPHeader header : ureq.getHeaders())
			log.info("[HTTPHeader]" + header.getName() + " = " + header.getValue());

		StringBuffer buf = new StringBuffer();
		StringBuffer logBuf = new StringBuffer();
		HTTPResponse res = null;
		int retries = 0;
		int resCode = -1;
		while(retries <= MAX_RETRIES){
			retries++;
			log.info("take " + retries);

			try{
				res = ursvc.fetch(ureq);

				if( (resCode = res.getResponseCode()) == HttpURLConnection.HTTP_OK){

					for(HTTPHeader httpHeader : res.getHeaders())
						logBuf.append("\n\t[HEADER : ").append(httpHeader.getName())
							.append("] = [").append(httpHeader.getValue()).append("]");

					log.info(logBuf.toString());
					
					buf.append(new String(res.getContent()));
					log.debug(buf.toString());

					break;
				}

			}catch(IOException ie){
				log.info("Exception:" + ie.getMessage());

				/*
				ByteArrayOutputStream bout = new ByteArrayOutputStream();
				PrintWriter pw = new PrintWriter(bout);
				ie.printStackTrace(pw);
				*/

				if(retries >= MAX_RETRIES)
					throw ie;
			}
			log.info("Post http_res:" + resCode);

		    try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		log.info("Post http_res:" + resCode);

		return buf.toString();
	}

	public static String doPost(String uri, Map<String,String> params) throws IOException{

		log.info("params = " + params.toString());

		if(uri != null)
			//return doPostByHttpClient(uri, params);
			return doPostByURLFetch(uri, params);

		URL url = new URL(uri);
		log.info("opening " + url.toString() + ", MAX_RETRIES = " + MAX_RETRIES);

		HttpURLConnection con = (HttpURLConnection) url.openConnection();
		con.setRequestMethod("POST");
		con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");  
		con.setDoOutput(true);
		//con.setInstanceFollowRedirects(false);
		con.setInstanceFollowRedirects(true);
		con.setConnectTimeout(10);
		con.setReadTimeout(10);

		for(String key : 	con.getRequestProperties().keySet())
			log.info(key + " = [" + con.getRequestProperty(key) + "]");
		
		con.connect();

		StringBuffer buf = new StringBuffer();
		OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream());
		boolean first = true;
		for(String paramName : params.keySet()){
			log.info(String.valueOf(first?"":"&") + URLEncoder.encode(paramName, "UTF-8")
					+ "=" + URLEncoder.encode(params.get(paramName), "UTF-8"));
			/*
			buf.append(String.valueOf(first?"":"&"))
				.append(URLEncoder.encode(paramName, "UTF-8"))
				.append("=")
				.append(URLEncoder.encode(paramName, "UTF-8"));
			*/
			
			writer.write(String.valueOf(first?"":"&") + URLEncoder.encode(paramName, "UTF-8")
					+ "=" + URLEncoder.encode(params.get(paramName), "UTF-8"));
			first = false;
		}
		//con.setRequestProperty("Content-Length", Integer.toString(buf.toString().getBytes().length));	
		writer.flush();
		writer.close();

		buf = new StringBuffer();
		int retries = 0;
		int resCode = -1;
		String line = null;
		String cookie = null;

		while(retries <= MAX_RETRIES){
			retries++;
			log.info("take " + retries);

			try{
				if( (resCode = con.getResponseCode()) == HttpURLConnection.HTTP_OK){

					log.info("Post http_res:" + resCode + " " + con.getResponseMessage());
					cookie = con.getHeaderField("Set-Cookie");
					log.info("Cookie:\n\t" + cookie);

					InputStream is = con.getInputStream();
					BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
					
					while((line = br.readLine()) != null)
			                buf.append(line).append("\n");

					break;
				}else{
					log.info("Post http_res:" + resCode + " " + con.getResponseMessage());
				}

			}catch(Exception ie){
				log.info("Exception:" + ie.getMessage());
				log.info(ie.toString());
			}
			log.info("Post http_res:" + resCode);

		    try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		con.disconnect();

		return buf.toString();
	}
	
	public static String doMultipartPost(String uri, Map<String,String> params, List<URL> fileList)throws IOException{

		return doMultipartPost(uri, params, fileList, "image_filename");
	}
	
	public static String doMultipartPost(String uri, Map<String,String> params, List<URL> fileList, String fileNameBase)throws IOException{
		Map<String, URL> fileMap = new HashMap<String, URL>();
		int cnt = 0;

		for(URL fileUrl : fileList){
			cnt++;
			fileMap.put(fileNameBase + cnt, fileUrl);
		}

		return doMultipartPost(uri, params, fileMap);
	}
		
	
	public static String doMultipartPost(String uri, Map<String,String> params, Map<String, URL> fileMap)throws IOException{

	  String boundary = Long.toHexString(System.currentTimeMillis());
	  final String CRLF = "\r\n";
	  final String charset = "utf-8";

	  //URLConnection connection = new URL(uri).openConnection();
	  HttpURLConnection connection = (HttpURLConnection) new URL(uri).openConnection();
	  connection.setDoOutput(true);
	  connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
	  PrintWriter writer = null;
	  try {
		    OutputStream output = connection.getOutputStream();
		    writer = new PrintWriter(new OutputStreamWriter(output, charset), true); // true = autoFlush, important!

		    // Send normal param.
			for(String paramName : params.keySet()){
				log.info("  " + paramName + "=" + params.get(paramName));

			    writer.append("--" + boundary).append(CRLF);
			    writer.append("Content-Disposition: form-data; name=\"" + paramName + "\"").append(CRLF);
			    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
			    writer.append(CRLF);
			    writer.append(params.get(paramName)).append(CRLF).flush();
			}

			URL binaryFile = null;
			//String fileName = "";
			for(String fileName : fileMap.keySet()) {

				log.info("  filename=\"" + fileName + "\"");
				// Send binary file.
			    writer.append("--" + boundary).append(CRLF);
			    
			    //writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + fileNameBase + cnt + "\"").append(CRLF);
			    writer.append("Content-Disposition: form-data; name=\"" + fileName + "\"; filename=\"" + fileName + ".JPG\"").append(CRLF);
			    //writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
			    writer.append("Content-Type: image/jpeg").append(CRLF);
			    //writer.append("Content-Transfer-Encoding: binary").append(CRLF);
			    writer.append(CRLF).flush();

			    InputStream input = null;
			    int fileSize = 0;
			    try {
			    	 binaryFile = fileMap.get(fileName);
			        input = binaryFile.openStream();
			        byte[] buffer = new byte[1024];
			        for (int length = 0; (length = input.read(buffer)) > 0;){
			            output.write(buffer, 0, length);
			            fileSize += length;
			        }

			        output.flush(); // Important! Output cannot be closed. Close of writer will close output as well.

			    } finally {
			        if (input != null) try { input.close(); } catch (IOException logOrIgnore) {}
			    }
			    writer.append(CRLF).flush(); // CRLF is important! It indicates end of binary boundary.
			   // writer.append("Content-Length: " + fileSize).append(CRLF);
			    log.info("  Content-Length=\"" + fileSize + "\"");

			    // Send text file.
			    /*
			    writer.append("--" + boundary).append(CRLF);
			    writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF);
			    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
			    writer.append(CRLF).flush();
			    BufferedReader reader = null;
			    try {
			        reader = new BufferedReader(new InputStreamReader(new FileInputStream(textFile), charset));
			        for (String line; (line = reader.readLine()) != null;) {
			            writer.append(line).append(CRLF);
			        }
			    } finally {
			        if (reader != null) try { reader.close(); } catch (IOException logOrIgnore) {}
			    }
			    writer.flush();
			    */
			}

		    // End of multipart/form-data.
		    writer.append("--" + boundary + "--").append(CRLF);
		} finally {
		    if (writer != null) writer.close();
		}
		
		StringBuffer buf = new StringBuffer();
		try{
			int http_res = connection.getResponseCode();
			log.info("Multipart Post http_res:" + http_res);

			if (http_res == HttpURLConnection.HTTP_OK) {
			    // OK
				BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(),"UTF-8"));
				String line = null;
				while((line = br.readLine()) != null)
				        buf.append(line).append("\n");
			}
		}catch(Exception ex){}
		
		log.debug("Multipart Post Response:\n" + buf.toString());
		return buf.toString();
	}
}
